package com.xyht.sca_s.student_manage_system.modules.assetConsumable.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonCodeEnum;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonResult;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ExceptionCast;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ResponseResult;
import com.xyht.sca_s.student_manage_system.common.util.CacheUtil;
import com.xyht.sca_s.student_manage_system.modules.asset.entity.SmsAssetCategoryManagement;
import com.xyht.sca_s.student_manage_system.modules.asset.entity.resp.SmsAssetUserResp;
import com.xyht.sca_s.student_manage_system.modules.asset.mapper.SmsAssetCategoryManagementMapper;
import com.xyht.sca_s.student_manage_system.modules.asset.util.AssetUtil;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.entity.SmsAssetConsumableStorage;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.entity.req.SmsAssetConsumableStorageBatchReq;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.entity.req.SmsAssetConsumableStorageReq;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.entity.resp.SmsAssetConsumableStorageResp;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.mapper.SmsAssetConsumableStorageMapper;
import com.xyht.sca_s.student_manage_system.modules.assetConsumable.service.SmsAssetConsumableStorageService;
import com.xyht.sca_s.student_manage_system.modules.common.entity.SmsImportTask;
import com.xyht.sca_s.student_manage_system.modules.common.entity.SmsImportTaskDetail;
import com.xyht.sca_s.student_manage_system.modules.common.mapper.SmsImportTaskDetailMapper;
import com.xyht.sca_s.student_manage_system.modules.common.mapper.SmsImportTaskMapper;
import com.xyht.sca_s.student_manage_system.modules.user.entity.SmsUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import static com.xyht.sca_s.student_manage_system.common.util.ParamCheckUtil.isNullOrEmpty;
import static com.xyht.sca_s.student_manage_system.modules.asset.constant.AssetStatusConstant.ASSET_STATUS_IN_STORAGE;
import static com.xyht.sca_s.student_manage_system.modules.asset.constant.InStorageMethodConstant.IN_STORAGE_METHOD_CONSUMABLE_IMPORT;
import static com.xyht.sca_s.student_manage_system.modules.asset.constant.InStorageMethodConstant.IN_STORAGE_METHOD_CONSUMABLE_SINGLE;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskDetailReason.*;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskNameExample.TASK_NAME_ASSET_CONSUMABLE_IMPORT;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskStatus.TASK_STATUS_DONE;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskStatus.TASK_STATUS_ERR;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskTypeConstant.TASK_TYPE_ASSET_CONSUMABLE;

/**
 * <p>
 * 易耗品资产库 服务实现类
 * </p>
 *
 * @author XYHT
 * @since 2023-06-27
 */
@Service
public class SmsAssetConsumableStorageServiceImpl extends ServiceImpl<SmsAssetConsumableStorageMapper, SmsAssetConsumableStorage> implements SmsAssetConsumableStorageService {
    @Autowired
    private SmsAssetConsumableStorageMapper smsAssetConsumableStorageMapper;
    @Autowired
    private SmsAssetCategoryManagementMapper smsAssetCategoryManagementMapper;
    @Autowired
    private CacheUtil cacheUtil;
    @Autowired
    private SmsImportTaskMapper smsImportTaskMapper;
    @Autowired
    private AssetUtil assetUtil;
    @Autowired
    private SmsImportTaskDetailMapper smsImportTaskDetailMapper;

    @Override
    @Transactional
    public ResponseResult singleAddConsumableStorage(SmsAssetConsumableStorageReq consumableStorageReq, String userId) {
        //校验必填项
        if (isNullOrEmpty(consumableStorageReq.getConsumableName()) || isNullOrEmpty(consumableStorageReq.getConsumableCategory()) ||
                isNullOrEmpty(consumableStorageReq.getConsumableWarehouse()) || isNullOrEmpty(consumableStorageReq.getConsumablePrice()) ||
                consumableStorageReq.getConsumableNum() <= 0) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        //校验分类是否合法
        SmsAssetCategoryManagement assetCategoryManagement = smsAssetCategoryManagementMapper.selectById(consumableStorageReq.getConsumableCategory());
        if (assetCategoryManagement == null || isNullOrEmpty(assetCategoryManagement.getId())) {
            return CommonResult.failed(CommonCodeEnum.ASSET_CATEGORY_NOT_EXIST);
        }

        //判断是否为根类别
        if (assetCategoryManagement.getId().length() < 3) {
            return CommonResult.failed(CommonCodeEnum.ASSET_CATEGORY_CAN_NOT_USE_ROOT);
        }

        //校验单价
        if (consumableStorageReq.getConsumablePrice().compareTo(BigDecimal.ZERO) < 0) {
            return CommonResult.failed(CommonCodeEnum.CONSUMABLE_PRICE_ERR);
        }

        //创建易耗品资产
        try {
            SmsAssetConsumableStorage smsAssetConsumableStorage = new SmsAssetConsumableStorage();
            BeanUtils.copyProperties(consumableStorageReq, smsAssetConsumableStorage);
            smsAssetConsumableStorage.setAddUserId(userId);
            smsAssetConsumableStorage.setConsumableStatus(ASSET_STATUS_IN_STORAGE);
            //设置物品id
            String assetId = assetUtil.createAssetStorageID(consumableStorageReq.getConsumableCategory(), new Date(), IN_STORAGE_METHOD_CONSUMABLE_SINGLE, 0);
            smsAssetConsumableStorage.setId(assetId);
            //设置总价
            Integer consumableNum = consumableStorageReq.getConsumableNum();
            BigDecimal totalPrice = consumableStorageReq.getConsumablePrice().multiply(BigDecimal.valueOf(consumableNum));
            smsAssetConsumableStorage.setTotalPrice(totalPrice);

            smsAssetConsumableStorageMapper.insert(smsAssetConsumableStorage);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.FAIL));
        }
        return CommonResult.success();
    }

    @Override
    public ResponseResult getConsumableStorage(Integer pageNum, Integer pageSize, String searchStr, String consumableCategory, String consumableWarehouse) {
        Page<SmsAssetConsumableStorage> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<SmsAssetConsumableStorage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (!isNullOrEmpty(consumableCategory)) {
            lambdaQueryWrapper.eq(SmsAssetConsumableStorage::getConsumableCategory, consumableCategory);
        }
        if (!isNullOrEmpty(consumableWarehouse)) {
            lambdaQueryWrapper.eq(SmsAssetConsumableStorage::getConsumableWarehouse, consumableWarehouse);
        }
        if (!isNullOrEmpty(searchStr)) {
            lambdaQueryWrapper.and(lambdaQuery ->
                    lambdaQuery.like(SmsAssetConsumableStorage::getConsumableName, searchStr)
                            .or()
                            .like(SmsAssetConsumableStorage::getConsumableCategory, searchStr)
                            .or()
                            .like(SmsAssetConsumableStorage::getConsumableBrand, searchStr)
                            .or()
                            .like(SmsAssetConsumableStorage::getConsumablePrice, searchStr)
                            .or()
                            .like(SmsAssetConsumableStorage::getId, searchStr)
                            .or()
                            .like(SmsAssetConsumableStorage::getConsumableWarehouse, searchStr));
        }

        lambdaQueryWrapper.orderByAsc(SmsAssetConsumableStorage::getConsumableStatus).orderByDesc(SmsAssetConsumableStorage::getCreateTime);

        smsAssetConsumableStorageMapper.selectPage(page, lambdaQueryWrapper);

        List<SmsAssetConsumableStorageResp> list = page.getRecords().stream().map(consumable -> {
            SmsAssetConsumableStorageResp consumableStorageResp = new SmsAssetConsumableStorageResp();
            BeanUtils.copyProperties(consumable, consumableStorageResp);

            //设置分类名称
            SmsAssetCategoryManagement categoryManagement = smsAssetCategoryManagementMapper.selectById(consumable.getConsumableCategory());
            if (categoryManagement != null) {
                consumableStorageResp.setConsumableCategoryName(categoryManagement.getCategoryName());
            }

            SmsUser smsUser = cacheUtil.getUserInfo(consumable.getAddUserId());
            if (smsUser != null) {
                SmsAssetUserResp smsAssetUserResp = new SmsAssetUserResp();
                BeanUtils.copyProperties(smsUser, smsAssetUserResp);
                consumableStorageResp.setUserInfo(smsAssetUserResp);
            }

            return consumableStorageResp;
        }).collect(Collectors.toList());

        int total = (int) page.getTotal();
        return CommonResult.success(list, total);
    }

    @Override
    @Async
    public Future<ResponseResult> batchAddConsumableStorage(Map<String, Object> map, String userId) {
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(map));
        List<SmsAssetConsumableStorageBatchReq> consumableStorageList = JSONObject.parseArray(JSON.toJSONString(jsonObject.get("list")), SmsAssetConsumableStorageBatchReq.class);
        if (consumableStorageList == null || consumableStorageList.size() < 1) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
        }
        //创建导入任务
        SmsImportTask smsImportTask = new SmsImportTask();
        smsImportTask.setTaskName(TASK_NAME_ASSET_CONSUMABLE_IMPORT);
        smsImportTask.setUserId(userId);
        smsImportTask.setTaskType(TASK_TYPE_ASSET_CONSUMABLE);
        smsImportTaskMapper.insert(smsImportTask);

        batchAddFixedStorage(consumableStorageList, smsImportTask.getId(), userId);

        return new AsyncResult<>(CommonResult.success());
    }

    @Resource
    PlatformTransactionManager transactionManager;

    private void batchAddFixedStorage(List<SmsAssetConsumableStorageBatchReq> consumableStorageList, String taskId, String userId) {
        int OFFSET_ROW = 2;
        Date nowDate = new Date();
        Integer task_finish_status = TASK_STATUS_DONE;

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = simpleDateFormat.format(nowDate);

        //获取当天批次
        Integer count = smsImportTaskMapper.selectCount(new LambdaQueryWrapper<SmsImportTask>().like(SmsImportTask::getCreateTime, dateStr));

        for (int i = 0; i < consumableStorageList.size(); i++) {
            //开启事务
            DefaultTransactionDefinition dt = new DefaultTransactionDefinition();
            //嵌套事务 PROPAGATION_REQUIRES_NEW 每次开启一个新的事务
            dt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            //设置嵌套事务
            TransactionStatus status = transactionManager.getTransaction(dt);

            //校验当前物品必填项是否填写完毕
            SmsAssetConsumableStorageBatchReq storage = consumableStorageList.get(i);
            if (isNullOrEmpty(storage.getConsumableBrand()) || isNullOrEmpty(storage.getConsumableCategoryName())
                    || isNullOrEmpty(storage.getConsumableModel()) || isNullOrEmpty(storage.getConsumableName())
                    || isNullOrEmpty(storage.getConsumableNum()) || isNullOrEmpty(storage.getConsumablePrice())) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_INVALID_PARAM, String.valueOf(i + OFFSET_ROW));
                continue;
            }

            //校验分类是否存在
            SmsAssetCategoryManagement categoryManagement =
                    smsAssetCategoryManagementMapper.selectOne(new LambdaQueryWrapper<SmsAssetCategoryManagement>()
                            .eq(SmsAssetCategoryManagement::getCategoryName, storage.getConsumableCategoryName()));
            if (categoryManagement == null || isNullOrEmpty(categoryManagement.getId())) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_ASSET_CATEGORY_NOT_EXIST, String.valueOf(i + OFFSET_ROW));
                continue;
            }

            //判断是否为根类别
            if (categoryManagement.getId().length() < 3) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_ASSET_CATEGORY_CAN_NOT_USE_ROOT, String.valueOf(i + OFFSET_ROW));
                continue;
            }

            //判断数量
            if (storage.getConsumableNum().equals(0)) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_ASSET_NUM_ERR, String.valueOf(i + OFFSET_ROW));
                continue;
            }

            //校验单价
            if (storage.getConsumablePrice().compareTo(BigDecimal.ZERO) < 0) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_ASSET_PRICE_ERR, String.valueOf(i + OFFSET_ROW));
                continue;
            }

            //易耗品入库
            try {
                SmsAssetConsumableStorage smsAssetConsumableStorage = new SmsAssetConsumableStorage();
                BeanUtils.copyProperties(storage, smsAssetConsumableStorage);
                smsAssetConsumableStorage.setAddUserId(userId);
                smsAssetConsumableStorage.setConsumableStatus(ASSET_STATUS_IN_STORAGE);
                smsAssetConsumableStorage.setConsumableCategory(categoryManagement.getId());
                //设置物品id
                String assetId = assetUtil.createAssetStorageID(categoryManagement.getId(), new Date(), IN_STORAGE_METHOD_CONSUMABLE_IMPORT, count + 1);
                smsAssetConsumableStorage.setId(assetId);
                //设置总价
                Integer consumableNum = storage.getConsumableNum();
                BigDecimal totalPrice = storage.getConsumablePrice().multiply(BigDecimal.valueOf(consumableNum));
                smsAssetConsumableStorage.setTotalPrice(totalPrice);
                //插入数据
                smsAssetConsumableStorageMapper.insert(smsAssetConsumableStorage);

                transactionManager.commit(status);
            } catch (Exception e) {
                transactionManager.rollback(status);
                task_finish_status = TASK_STATUS_ERR;
                createTeacherImportTaskDetail(taskId, REASON_IMPORT_ERR, String.valueOf(i + OFFSET_ROW));
            }
        }
        SmsImportTask smsImportTask = new SmsImportTask();
        smsImportTask.setId(taskId);
        smsImportTask.setStatus(task_finish_status);
        smsImportTaskMapper.updateById(smsImportTask);
    }

    private void createTeacherImportTaskDetail(String taskId, String reason, String row) {
        SmsImportTaskDetail smsImportTaskDetail = new SmsImportTaskDetail();
        smsImportTaskDetail.setTaskId(taskId);
        smsImportTaskDetail.setFailReason(reason);
        smsImportTaskDetail.setFailRow(row);
        smsImportTaskDetailMapper.insert(smsImportTaskDetail);
    }
}
